#include <cmath>
#include "geometry.h"
#include "log.h"

namespace common
{

	CRect::CRect()
	{
		xmin_ = 0;
		xmax_ = 0;
		zmin_ = 0;
		zmax_ = 0;
	}

	CRect::CRect(float xmin, float xmax, float zmin, float zmax)
	{
		xmin_ = xmin;
		xmax_ = xmax;
		zmin_ = zmin;
		zmax_ = zmax;
	}

	CRect::~CRect()
	{
	}

	bool CRect::IsContain(float x, float z) const
	{
		if (x < xmin_ || x > xmax_ || z < zmin_ || z > zmax_)
		{
			return false;
		}
		else
		{
			return true;
		}
	}

	bool CRect::IsContain(const CVector3& point) const
	{
		return IsContain(point.x(), point.z());
	}

	bool CCircle::IsContain(const CVector3& point) const
	{
		return WithinDistance(origin_point_, point, radius_);
	}


	CVector3 CCircle::GetPointAtArc(float angle) const
	{
		CVector3 point;
		float x1 = origin_point_.x() + radius_ * ::cos(angle * M_PI / 180);
		float z1 = origin_point_.z() + radius_ * ::sin(angle * M_PI / 180);

		point.set_x(x1);
		point.set_z(z1);

		point.set_y(origin_point_.y());
		return point;
	}

	bool CPolygon::IsContain(const CVector3& point) const
	{
		if (points_.size() < 3)
		{
			return false;
		}
		CVector3 vmin = points_[0];
		CVector3 vmax = points_[1];
		for (int32_t i = 0; i < (int32_t)points_.size(); ++i)
		{
			if (vmin.x() > points_[i].x())
			{
				vmin.set_x(points_[i].x());
			}
			if (vmin.z() > points_[i].z())
			{
				vmin.set_z(points_[i].z());
			}
			if (vmax.x() < points_[i].x())
			{
				vmax.set_x(points_[i].x());
			}
			if (vmax.z() < points_[i].z())
			{
				vmax.set_z(points_[i].z());
			}
		}
		if ((point.x() < vmin.x()) || (point.z() < vmin.z()) || (point.x() > vmax.x()) || (point.z() > vmax.z()))
		{
			return false;
		}

		int32_t intersection = 0;
		for (int32_t i = 0; i < (int32_t)points_.size(); ++i)
		{
			const CVector3& p1 = points_[i];
			const CVector3& p2 = points_[(i + 1) % points_.size()];
			if (((p1.z() - point.z()) <= 0.0f) && ((p2.z() - point.z()) <= 0.0f))
			{
				continue;
			}
			if (((p1.z() - point.z()) > 0.0f) && ((p2.z() - point.z()) > 0.0))
			{
				continue;
			}
			float xinter = p1.x() + (p2.x() - p1.x()) * ((point.z() - p1.z()) / (p2.z() - p1.z()));
			if (xinter > point.x())
			{
				++intersection;
			}
		}
		return ((intersection & 1) == 1);//奇数个交点
	}

	int CompareFloat(float data1, float data2)
	{
		if (fabs(data1 - data2) <= DOUBLE_ERROR)
		{
			return 0;
		}
		else
		{
			if (data1 - data2 > DOUBLE_ERROR)
			{
				return 1;
			}
			else
			{
				return -1;
			}
		}
	}

	int CompareDouble(double data1, double data2)
	{
		if (fabs(data1 - data2) <= DOUBLE_ERROR)
		{
			return 0;
		}
		else
		{
			if (data1 - data2 > DOUBLE_ERROR)
			{
				return 1;
			}
			else
			{
				return -1;
			}
		}
	}

	int GetQuadrant(const CVector3& point)
	{
		if (point.x() > 0)
		{
			if (point.z() >= 0)
			{
				return 0;
			}
			else
			{
				return 3;
			}
		}
		else if (point.x() == 0)
		{
			if (point.z() > 0)
			{
				return 1;
			}
			else if (CompareFloat(point.z(), 0.0f) == 0)
			{
				return 4;
			}
			else
			{
				return 3;
			}
		}
		else
		{
			if (point.z() > 0)
			{
				return 1;
			}
			else
			{
				return 2;
			}
		}
	}

	CVector3 VerticalVector(const CVector3& ivector)
	{
		CVector3 vertical_vector;
		vertical_vector.set_x(-ivector.z());
		//vertical_vector.set_y(ivector.y());
		vertical_vector.set_z(ivector.x());
		return vertical_vector;
	}

	CVector3 CalcDirectionVector(const CVector3& start_point, const CVector3& end_point)
	{
		CVector3 direction_vector;
		direction_vector.set_x(end_point.x() - start_point.x());
		direction_vector.set_z(end_point.z() - start_point.z());
		return direction_vector;
	}

	float CalcCross(const CVector3& point1, const CVector3& point2, const CVector3& point3)
	{
		return (point2.x() - point1.x()) * (point3.z() - point1.z()) - (point2.z() - point1.z()) * (point3.x() - point1.x());
	}

	float MyDistance(const CVector3& cur, const CVector3& target)
	{
		return sqrt((cur.x() - target.x()) * (cur.x() - target.x()) + (cur.z() - target.z()) * (cur.z() - target.z()));
	}

	bool WithinDistance(const CVector3& cur, const CVector3& target, float distance)
	{
		return ((cur.x() - target.x()) * (cur.x() - target.x()) + (cur.z() - target.z()) * (cur.z() - target.z())) <= (distance * distance);
	}

	bool WithinDistanceEx(const CVector3& cur, const CVector3& target, float distance)
	{
		if (fabs(cur.x() - target.x()) > distance || fabs(cur.z() - target.z()) > distance)
		{
			return false;
		}
		return true;
	}

	bool IsCrossSegmentWithCircle(const CVector3& start_point, const CVector3& end_point, const CCircle& circle)
	{
		int start_compare = CompareFloat(MyDistance(start_point, circle.origin_point()), circle.radius());
		int end_compare = CompareFloat(MyDistance(end_point, circle.origin_point()), circle.radius());

		// 有一个在圆上，相交
		if (start_compare == 0 || end_compare == 0)
		{
			return true;
		}

		// 都在圆内，不相交
		if (start_compare < 0 && end_compare < 0)
		{
			return false;
		}

		// 求圆心到线段的最近点
		CVector3 dir = end_point - start_point;
		if (CompareFloat(dir.sqr_length(), 0.0f) <= 0)
		{
			return false;
		}

		CVector3 origin_dir = circle.origin_point() - start_point;

		// 计算点乘
		float dot = origin_dir.x() * dir.x() + origin_dir.z() * dir.z();
		dot /= dir.sqr_length();

		// 限制在 [ 0, 1 ] 之间
		dot = CompareFloat(dot, 0.0f) < 0 ? 0.0f : CompareFloat(dot, 1.0f) > 0 ? 1.0f : dot;

		CVector3 closest_point = start_point + dir * dot;
		if (MyDistance(closest_point, circle.origin_point()) <= circle.radius())
		{
			return true;
		}

		return false;
	}

	bool IsCrossShapeWithCircle(const CShape& shape, const CCircle& circle)
	{
		if (shape.shape_type() == SHAPE_TYPE_CIRCLE)
		{
			const CCircle* shape_circle = dynamic_cast<const CCircle*>(&shape);
			if (NULL == shape_circle)
			{
				return false;
			}

			return WithinDistance(shape_circle->origin_point(), circle.origin_point(), shape_circle->radius() + circle.radius());
		}
		else if (shape.shape_type() == SHAPE_TYPE_POLYGON)
		{
			const CPolygon* polygon = dynamic_cast<const CPolygon*>(&shape);
			if (NULL == polygon)
			{
				return false;
			}

			const std::vector<CVector3>& points = polygon->points();
			for (std::vector<CVector3>::const_iterator itr = points.begin(); itr != points.end() - 1; ++itr)
			{
				if (IsCrossSegmentWithCircle(*itr, *(itr + 1), circle) == true)
				{
					return true;
				}
			}
		}
		else
		{
			LOG_ERROR("default", "shape type[%d] not support !!!!!", shape.shape_type());
		}

		return false;
	}
}
